using B.S.RbacData.Domain;
using B.S.RbacData.ErrorCode;
using B.S.RbacData.Infrastructure;
using B.S.RbacData.Read.API.Applications.Commands;
using B.S.RbacData.Read.API.Dtos;
using MediatR;
using Microsoft.EntityFrameworkCore;
using Microsoft.IdentityModel.Tokens;
using Newtonsoft.Json;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;

namespace B.S.RbacData.Read.API.Applications.CommandHandlters
{
    /// <summary>
    /// 刷新Token命令处理器
    /// </summary>
    public class RefreshTokenCommandHandlter : IRequestHandler<RefreshTokenCommand, ApiResult<TokenResponse>>
    {
        private readonly IConfiguration _configuration;
        private readonly IBaseRepository<UserModel> _userRepository;

        public RefreshTokenCommandHandlter(IConfiguration configuration, IBaseRepository<UserModel> userRepository)
        {
            _configuration = configuration;
            _userRepository = userRepository;
        }

        public async Task<ApiResult<TokenResponse>> Handle(RefreshTokenCommand request, CancellationToken cancellationToken)
        {
            if (string.IsNullOrEmpty(request.AccessToken) || string.IsNullOrEmpty(request.RefreshToken))
            {
                return new ApiResult<TokenResponse>
                {
                    code = ResultEnumCode.fails,
                    message = "令牌不能为空",
                    data = null
                };
            }

            try
            {
                // 从过期的access token中获取principal
                var principal = GetPrincipalFromExpiredToken(request.AccessToken);
                if (principal == null)
                {
                    return new ApiResult<TokenResponse>
                    {
                        code = ResultEnumCode.fails,
                        message = "无效的令牌",
                        data = null
                    };
                }

                var userId = int.Parse(principal.FindFirst("UserId").Value);
                var userName = principal.FindFirst("UserName").Value;

                // 验证refresh token是否有效
                var storedRefreshToken = JsonConvert.DeserializeObject<RefreshToken>(request.RefreshToken);
                if (storedRefreshToken == null || storedRefreshToken.Expires < DateTime.UtcNow)
                {
                    return new ApiResult<TokenResponse>
                    {
                        code = ResultEnumCode.fails,
                        message = "刷新令牌已过期",
                        data = null
                    };
                }

                // 验证用户是否存在
                var user = await _userRepository.GetAll().FirstOrDefaultAsync(x => x.ID == userId && !x.IsDeleted, cancellationToken);
                if (user == null)
                {
                    return new ApiResult<TokenResponse>
                    {
                        code = ResultEnumCode.fails,
                        message = "用户不存在",
                        data = null
                    };
                }

                // 生成新的token
                var newToken = CreateTokenString(userName, userId);

                return new ApiResult<TokenResponse>
                {
                    code = ResultEnumCode.Ok,
                    message = "令牌刷新成功",
                    data = newToken
                };
            }
            catch (Exception ex)
            {
                return new ApiResult<TokenResponse>
                {
                    code = ResultEnumCode.fails,
                    message = $"刷新令牌错误: {ex.Message}",
                    data = null
                };
            }


        }

        /// <summary>
        ///  解析token
        /// </summary>
        /// <param name="token"></param>
        /// <returns></returns>
        /// <exception cref="SecurityTokenException"></exception>
        private ClaimsPrincipal GetPrincipalFromExpiredToken(string token)
        {
            var tokenValidationParameters = new TokenValidationParameters
            {
                ValidateAudience = false,
                ValidateIssuer = false,
                ValidateIssuerSigningKey = true,
                IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration["JwtSettings:SecretKey"])),
                ValidateLifetime = false // 这里我们就是要处理过期的token
            };

            var tokenHandler = new JwtSecurityTokenHandler();
            var principal = tokenHandler.ValidateToken(token, tokenValidationParameters, out var securityToken);

            if (securityToken is not JwtSecurityToken jwtSecurityToken ||
                !jwtSecurityToken.Header.Alg.Equals(SecurityAlgorithms.HmacSha256, StringComparison.InvariantCultureIgnoreCase))
                throw new SecurityTokenException("Invalid token");

            return principal;
        }

        /// <summary>
        /// 创建新的Token
        /// </summary>
        /// <param name="userName">用户名</param>
        /// <param name="userId">用户ID</param>
        /// <returns>Token响应</returns>
        private TokenResponse CreateTokenString(string userName, int userId)
        {
            var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration["JwtSettings:SecretKey"]));
            var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256);

            var claims = new[]
            {
                new Claim("UserName", userName),
                new Claim("UserId", userId.ToString()),
                new Claim("RoleId", "1"),
                new Claim("RoleName", "")
            };

            var token = new JwtSecurityToken(
                issuer: _configuration["JwtSettings:Issuer"],
                audience: _configuration["JwtSettings:Audience"],
                claims: claims,
                expires: DateTime.Now.AddMinutes(Convert.ToDouble(_configuration["JwtSettings:AccessTokenExpirationMinutes"])),
                signingCredentials: credentials
            );

            // 生成授权token字符串
            var strToken = new JwtSecurityTokenHandler().WriteToken(token);

            // 生成续期Token
            var refreshToken = new RefreshToken
            {
                Token = Guid.NewGuid().ToString("N"),
                Expires = DateTime.UtcNow.AddDays(Convert.ToDouble(_configuration["JwtSettings:RefreshTokenExpirationDays"])),
                Created = DateTime.UtcNow,
                UserId = userId
            };

            return new TokenResponse()
            {
                AccessToken = strToken,
                RefreshToken = JsonConvert.SerializeObject(refreshToken)
            };
        }
    }
} 